//#region interface

interface ClassData {
    reqList: any[];
    resObj: any;
    summary: string;
    url: string;
    method: string;
    returnCodeList: ReturnCodeData[];
}
interface ReturnCodeData {
    code: number;
    description: string;
}

interface IParam {
    name: string;
    in: string;
    description: string;
    //@ts-ignore
    schema: { type: string; }
}

interface IResPropBase {
    type: string;
}
interface IResProp extends IResPropBase {
    properties?: Record<string, any>;
    name: string;
    description: string;
}

interface IResArrayProp extends IResPropBase {
    properties?: Record<string, any>;
    name: string;
    type: 'array';
    items: {
        properties: Record<string, IResProp>;
    }
}

//#endregion interface
//@ts-ignore
const fs = require('fs');
const varTemp = `
\t\t/** {desc} */
\t\t{varName} : {varType};`;
const interfaceTemp = `\t/** {desc} */
\tinterface {name} {
{varList} 
\t}\n`;
function isArray(params: IResPropBase) {
    return params && params.type === 'array';
}
function isObject(params: IResPropBase) {
    return params && params.type === 'object';
}

//MARK: 解析返回数据
function getRes(json: any) {
    try {
        let schemaData = json.responses[200]['content']['application/json'];
        if (!schemaData) {
            schemaData = json.responses[200]['content']['*/*'];
        }
        if (schemaData.schema.properties) {
            return parseResProperties(schemaData.schema.properties);
        } else {
            console.log('parse schema');
        }
    } catch (error) {
        console.log('get res error', error);
    }
    return undefined;
}

function parseResProperties(propsObj: any) {
    const res: any = {};
    let hasKey = false;
    for (const key in propsObj) {
        const item: IResProp | IResArrayProp = propsObj[key];
        switch (key) {
            //忽略这两个值
            case 'code':
            case 'msg':

                break;

            default:
                hasKey = true;
                if (isArray(item)) {
                    res[key] = parseArray(key, (item as IResArrayProp).items.properties);
                } else {
                    res[key] = parseObject(key, item.properties as any);
                }
                break;
        }
    }

    if (hasKey) {
        return res;
    }
}



function parseArray(key: string, map: Record<string, any>) {
    const ret: any = { name: key, list: [], type: 'array' };

    for (const key in map) {
        const item = map[key];
        item.name = key;
        if (isObject(item)) {
            const object = parseObject(key, item.properties);
            ret.list.push(object);
        } else if (isArray(item)) {
            if (item.items.properties) {
                //@ts-ignore
                const array = parseObject(key, item.items.properties);
                // array.type + '[]'
                ret.list.push(array);
            } else {

                const data: IResProp = {
                    name: key,
                    description: item.description,
                    type: item.items.type + '[]',
                }
                ret.list.push(data);
            }
        } else {
            const data: IResProp = {
                name: key,
                description: item.description,
                type: item.type,
            }
            ret.list.push(data);
        }
    }
    return ret;
}

function parseObject(varName: string, object: Record<string, IResProp>) {
    const ret: any = { name: varName, list: [] };
    for (const key in object) {
        const item = object[key];
        item.name = key;

        if (isObject(item)) {
            const object = parseObject(key, item.properties as any);
            ret.list.push(object);
        } else if (isArray(item)) {
            const object = parseArray(key, (item as any).items.properties as any);
            ret.list.push(object);
        } else {
            const data: IResProp = {
                name: key,
                description: item.description,
                type: item.type,
            }
            ret.list.push(data);
        }
    }
    return ret;
}

function toTsType(type: string, desc: string) {
    const reg = /[a-zA-Z0-9\s\|]+/;
    switch (type) {
        case 'integer':
        case 'page':
            return 'number';

        case 'text':
        case 'string':
            if (reg.test(desc)) {
                const regRet: any[] = desc.match(reg) as any;
                const list: string[] = regRet[0].split('|');
                for (let i = 0; i < list.length; i++) {
                    let item = list[i];
                    item = item.replace(/\s/g, '');
                    list[i] = "'" + item + "'";
                }
                return list.join('|');
            }
            return 'string';
        default:
            return type;
    }
}

function capitalizeFirstLetter(str) {
    return str ? str.charAt(0).toUpperCase() + str.slice(1) : '';
}
function url2InterfaceName(url: string) {
    let name = url.replace('/api/', '');
    name = name.replace(/^\//g, '');
    name = name.replace(/\//g, '_');
    return capitalizeFirstLetter(name);
}

function parseReqVars(reqList: any[]) {
    var varStrList: string[] = [];
    const list = reqList || [];
    list.forEach((item) => {
        let temp = varTemp.replace('{varName}', item.name);
        temp = temp.replace('{varType}', toTsType(item.schema.type, item.description));
        temp = temp.replace('{desc}', item.description);
        varStrList.push(temp);
    });
    return varStrList;
}

function parseResVars(resObj: any) {
    for (const key in resObj) {
        const item = resObj[key];
        const resVarList: any[] = item.list || [];
        const varStrList: string[] = [];
        resVarList.forEach((inner) => {
            let temp = varTemp.replace('{varName}', inner.name);
            temp = temp.replace('{varType}', toTsType(inner.type, inner.description));
            temp = temp.replace('{desc}', inner.description);
            varStrList.push(temp);
        });
        return varStrList;
    }
    return [];
}

function toDtsString(list: ClassData[]) {
    const retList: string[] = [];
    list.forEach((item) => {
        const className = url2InterfaceName(item.url);
        //#region parse req
        const reqVarStrList: string[] = parseReqVars(item.reqList);
        if (reqVarStrList.length > 0) {
            let interfaceReqStr = interfaceTemp.replace('{name}', className + 'Req');
            interfaceReqStr = interfaceReqStr.replace('{varList}', reqVarStrList.join('\n'));
            interfaceReqStr = interfaceReqStr.replace('{desc}', item.summary + ' 请求数据');
            retList.push(interfaceReqStr);
        }
        //#endregion parse req

        //#region parse res
        const resVarStrList: string[] = parseResVars(item.resObj);
        let interfaceResStr = interfaceTemp.replace('{name}', className + 'Res');
        interfaceResStr = interfaceResStr.replace('{varList}', resVarStrList.join('\n'));
        interfaceResStr = interfaceResStr.replace('{desc}', item.summary + ' 返回数据');
        retList.push(interfaceResStr);
        //#endregion parse res

        let interfaceHttpStr = interfaceTemp.replace('{name}', className + 'Http');
        interfaceHttpStr = interfaceHttpStr.replace('{desc}', item.summary);
        const url = `\t\turl: "${item.url}";\n`;
        const method = `\t\tmethod: "${item.method}";\n`;
        let resHttpStr = '';
        if (resVarStrList.length > 0) {
            let resType = `${className}Res`;

            if (item.resObj && item.resObj.result && item.resObj.result.type === 'array') {
                resType += '[]';
            }
            resHttpStr = `\t\tres?: Res<${resType}>;`;
        }
        let reqHttpStr = '';
        if (reqVarStrList.length > 0) {
            reqHttpStr = `\t\tdata: ${className}Req;\n`;
        }
        let typeCodeRet = returnCode2TS(className + 'Http', item.returnCodeList);
        let codeVar = '';
        if (typeCodeRet) {
            codeVar = '\t\tcode?: ' + typeCodeRet.name + ";\n";
            interfaceHttpStr = typeCodeRet.declare + interfaceHttpStr;
        }
        interfaceHttpStr = interfaceHttpStr.replace('{varList}', url + method + codeVar + reqHttpStr + resHttpStr);



        retList.push(interfaceHttpStr);
    });
    return retList;
}

//#region return code
function parseReturnCode(json: any) {
    const list: any[] = [];
    try {
        for (const key in json.responses) {
            const item = json.responses[key];
            const data = {
                code: parseInt(key),
                description: item.description,
            }
            list.push(data);
        }
    } catch (error) {
        console.log('parse return code error', error);
    }
    return list;
}

function returnCode2TS(name: string, list: ReturnCodeData[]) {
    try {
        const ret: any = {};
        const retList: string[] = [];
        ret.name = '';
        ret.declare = '';
        let str = '';
        const allList: string[] = [];
        list.forEach((item) => {
            str += `\t/** ${item.description} */\n`;
            const typeName = `${name}_${item.code}`;
            str += `\ttype ${typeName} = ${item.code};\n`;
            allList.push(typeName);
        });
        retList.push(str);
        const typeCode = `TypeCode_${name}`;
        let all = `\ttype ${typeCode} = ` + allList.join('|');
        ret.name = typeCode;
        ret.declare = retList.join('\n') + '\n' + all + ';\n';
        return ret;
    } catch (error) {

    }
    return undefined;
}
//#endregion return code


//@ts-ignore
const http = require('http');
function getHttpJson(callback: any) {
    http.get('http://www.chat.com/index/index/doc', (res) => {

        let data = '';
        res.on('data', (chunk) => {
            data += chunk;
        });

        res.on('end', () => {
            callback(JSON.parse(data));
        });
    }).on('error', (err) => {
        console.error('请求错误:', err);
        callback(undefined);
    });
}

function readLocalJson(callback: any) {
    try {
        const txt = fs.readFileSync('./feiwuxingren.json', 'utf8');
        const json = JSON.parse(txt);
        callback(json, true);

    } catch (error) {
        console.log('load local json error', error);
    }
}

function doRun(json: any, noWrite?: boolean) {
    const list: ClassData[] = [];
    try {
        if (!noWrite) {
            fs.writeFileSync('./api.json', JSON.stringify(json, null, 2), 'utf8');
        }
        for (const key in json.paths) {
            const item = json.paths[key];
            for (const i in item) {
                const inner = item[i];
                const obj: ClassData = { url: key.replace('/api', ''), method: i } as any;
                obj.reqList = [];
                if (inner.parameters) {
                    inner.parameters.forEach((param) => {
                        if (param.name !== 'user-token') {
                            obj.reqList.push(param);
                        }
                    })
                }
                obj.summary = inner.summary;
                obj.resObj = getRes(inner);
                obj.returnCodeList = parseReturnCode(inner);
                list.push(obj);

                // if (key == '/api/getrenmendazhi') {
                //     console.log(JSON.stringify(obj, null, 2));
                // }
            }
        }
        if (list.length > 0) {
            const retList = toDtsString(list);
            // if (retList.length > 0) {
            //     console.log(retList[0]);
            // }

            const namespaceHead = 'declare namespace msg.auto {\n';
            const namespaceTail = '}';

            const filePath = 'E:/workplace/same-city/charm-of-same-city/src/types/auto';
            fs.writeFileSync(filePath + '/msg_auto.d.ts', namespaceHead + retList.join('\n') + namespaceTail, 'utf-8');
        }

    } catch (error) {
        console.log('error', error);
    }
}
function run() {
    // getHttpJson(doRun);
    readLocalJson(doRun);
}
run();
